<?php

/**
 * Period Control Class
 * 
 *
 */
class Of_Propel_Period extends Of_Propel_Object 
{
  /**
   * start of period column name
   *
   * @var string
   */
  public static $startOfPeriod = "StartOfPeriod";
  
  protected $sop;
  
  /**
   * end of period column name
   *
   * @var string
   */
  public static $endOfPeriod = "EndOfPeriod";
  
  protected $eop;
  
  /**
   * date of end
   *
   * @var string
   */
  public static $complatedAt = null;
  
  protected $objectName;
  
  public function __construct($object)
  {
    parent::__construct($object);
    $this->sop = self::$startOfPeriod;
    $this->eop = self::$endOfPeriod;
    $this->objectName = get_class($object);
  }
  
  
  /**
   * period php name
   *
   * @param string $st
   * @param string $ed
   */
  public function setPhpNameStEd($st, $ed)
  {
    $this->sop = $st;
    $this->eop = $ed;
  }
  
  /**
   * create new period record
   * 
   * 
   */
  public function create()
  {
    $object = $this->object;
    $keys = func_get_args();
    
    if($this->validate($keys)===false)
    {
      throw new Of_Propel_Exception('period data is not valid');
    }
    

    $close_class = $this->objectName;

    $p = $object->toArray();

    foreach($keys as $key)
    {
      $params[$key] = $p[$key];
    }

    $st_const = $this->getStColname();

    $ed_const = $this->getEdColname();

    $st_param = Of_Date::newInstance($object->getByName($this->sop))->get('Y-m-d');
    $v = $object->getByName($this->sop);
    $ed_param = Of_Date::newInstance($v)->addDay(-1)->get('Y-m-d');//end of period before one record
    	
    $close_object = new $close_class;

    $close_object->fromArray($params);
    $c = $close_object->buildCriteria();
    $c->add($ed_const, self::$complatedAt);//empty or lastest date
    $c->add($st_const, $ed_param, Criteria::LESS_EQUAL);//
    $rs = $object->getPeer()->doSelect($c);
    foreach($rs as $ob)
    {
    
        $ob->setByName(
          $this->eop
          , $ed_param);
      
      $ob->save();
    }
    $object->setByName($this->eop, self::$complatedAt);
    return $object->save();
    
  }
  
  /**
   * 
   * @param Of_Propel_IObject $object
   * @param array $keys phpname=>$value
   * @param Criteria $c
   */
  public function addKeys($object, $keys, Criteria $c)
  {
    foreach($keys as $key )
    {
      $col = $this->object->getPeer()->translateFieldName(
        $key,
        BasePeer::TYPE_PHPNAME,
        BasePeer::TYPE_COLNAME
      );
      
      $c->add($col, $object->getByName($key));
      
    }
  }
  
  public function getStColname()
  {
    return $this->object->getPeer()->translateFieldName($this->sop, BasePeer::TYPE_PHPNAME, BasePeer::TYPE_FIELDNAME);
  }
  public function getEdColname()
  {
    return $this->object->getPeer()->translateFieldName($this->eop, BasePeer::TYPE_PHPNAME, BasePeer::TYPE_FIELDNAME);
  }
  
  /**
   * Update record. 
   * 
   * If period column is updated, before and after record update.
   * 
   * 
   */
  public function update()
  {
    $keys = func_get_args();
    
    $sop = $this->object->getByName($this->sop);
    $eop = $this->object->getByName($this->eop);
    
    if(!$this->object->getPrimaryKey())
    {
      throw new Of_Propel_Exception("primary key is null.");
    }
    
    if($this->object->getByName($this->sop)>= $this->eop)
    {
      throw new Of_Propel_Exception('start of period greater equal end of period');
    }
    
    
    $this->object->setNew(false);
    
    $oldObject = $this->object->getPeer()->retrieveByPk($this->object->getPrimaryKey());
    
    
    if($oldObject->getByName($this->eop) === self::$complatedAt)
    {
      $this->object->setByName($this->eop, self::$complatedAt);
    }
    // update before record's date that end of period
    if($oldObject->getByName($this->sop)!== $sop && $sop !== null)
    {
      $c = new Criteria();
      $this->addKeys($oldObject, $keys, $c);
      $c->addAnd($this->getStColname(), $oldObject->getByName($this->sop), Criteria::LESS_THAN);
      $c->addDescendingOrderByColumn($this->getStColname());
      
      $beforeObject = $oldObject->getPeer()->doSelectOne($c);
      if($beforeObject)
      {
        if($beforeObject->getByName($this->sop) >= $this->object->getByName($this->sop))
        {
          $bd = $beforeObject->getByName($this->sop);
          $nd = $this->object->getByName($this->sop);
          throw new Of_Propel_Exception("before record's start of period '{$bd}' is greater than updated start of period '$nd'");
        }
        $beforeDate = Of_Date::newInstance(
          $this->object->getByName($this->sop)
          )->addDay(-1)->get('Y-m-d');
        $beforeObject->setByName($this->eop
          ,  $beforeDate);
        $beforeObject->save();
      }
    }
    
    // update after record's date that start of period 
    if($oldObject->getByName($this->eop) !== $eop && $eop !== null && $oldObject->getByName($this->eop) !== null)
    {
      $c = new Criteria();
      $this->addKeys($oldObject, $keys, $c);
      $c->add($this->getEdColname(), $oldObject->getByName($this->eop), Criteria::GREATER_THAN);
      $c->addAscendingOrderByColumn($this->getStColname());
      if(self::$complatedAt === null)
      {
        $c->addOr($this->getEdColname(), null, Criteria::ISNULL);
      }
      
      
      $afterObject = $oldObject->getPeer()->doSelectOne($c);
      
      if($afterObject)
      {
        if($afterObject->getByName($this->eop) <= $eop)
        {
          $a_eof = $afterObject->getByName($this->eop);
          
          throw new Of_Propel_Exception("after record's end of period '{$a_eof}' is less than updated end of period {$eop}");
        }
        
        $afterDate = Of_Date::newInstance(
          $this->object->getByName($this->eop)
          )->addDay(-1)->get('Y-m-d');
        $afterObject->setByName($this->sop
          ,  $afterDate);
        $afterObject->save();
      }
      
      

      
    }
    
    return $this->object->save();
    
  }
  
  
  /**
   * validate all records by key
   *
   * @param unknown_type $keys
   * @return unknown
   */
  public function validate($keys)
  {
    $object = $this->object;
    $p = $object->toArray();
    
    $ob = new $this->objectName;
    
    foreach($keys as $key)
    {
      $ob->setByName($key, $object->getByName($key));
    }
    
    $rs = $ob->getPeer()->doSelect($ob->buildCriteria());
    
    $st = $object->getByName($this->sop);
    
    foreach($rs as $r)
    {
      
      if($st <= $r->getByName($this->eop) and $r->getByName($this->eop)!==self::$complatedAt)
      {
        return false;
      }
      
      if($st <= $r->getByName($this->sop))
      {
        return false;
      }
      
    }
    
    return true;
    
  }
  
  /**
   * 
   *
   * @param Criteria $c
   * @param string $date
   * @return Criteria
   */
  public function getMatchedCriteria($c = null, $date = null)
  {
    
    $st_const = $object->getPeer()->translateFieldName(
    $this->sop
    , BasePeer::TYPE_PHPNAME
    , BasePeer::TYPE_COLNAME
    );

    $ed_const = $object->getPeer()->translateFieldName(
    $this->eop
    , BasePeer::TYPE_PHPNAME
    , BasePeer::TYPE_COLNAME
    );
    
    Of_Propel_Criteria::newInstance($c)->addMatchedPeriod($date, $st_const, $ed_const);
    
    return $c;
  }
  
  
  /**
   * create new instance
   *
   * @param BaseObject $object
   * @return Of_Propel_Period
   */
  public static function newInstance(BaseObject $object)
  {
    return new self($object);
  }  
  
}